package com.mccken.springframework.beans.factory.support;

import java.lang.reflect.Constructor;

import com.mccken.springframework.beans.BeansException;
import com.mccken.springframework.beans.PropertyValue;
import com.mccken.springframework.beans.PropertyValues;
import com.mccken.springframework.beans.factory.config.BeanDefinition;
import com.mccken.springframework.beans.factory.config.BeanPostProcessor;
import com.mccken.springframework.beans.factory.config.BeanReference;

import cn.hutool.core.bean.BeanUtil;
import lombok.Getter;
import lombok.Setter;

/**
 * @program: mccken-collect
 * @description:
 * @author: mccken
 * @create: 2022-11-19 23:03
 **/
public abstract class AbstractAutowiredCapableBeanFactory extends AbstractBeanFactory {
	
	@Getter
	@Setter
	private InstantiationStrategy instantiationStrategy = new CglibSubclassingInstantiationstrategy();
	
	@Override
	protected Object createBean(String beanName, BeanDefinition beanDefinition, Object... args) throws BeansException {
		Object bean = null;
		try {
			bean = createBeanInstance(beanDefinition, beanName, args);
			// 给Bean对象填充属性 	
			applyPropertyValues(beanName, bean, beanDefinition);
			// 执行 bean对象的初始化方法和BeanPostProcessor接口的前置和后置处理方法
			bean = initializeBean(beanName, bean, beanDefinition);
			
		} catch(Exception e) {
			throw new BeansException("Instantiation of bean failed", e); 	
		}
		
		registerSingleton(beanName, bean);
		return bean;
	}

	private Object initializeBean(String beanName, Object bean, BeanDefinition beanDefinition) {
		// 1. 执行BeanPostProcessor Before前置处理
		Object wrappedBean = applyBeanPostProcessorsBeforeInitialization(bean, beanName);
		
		// 待完成的内容
		invokeInitMethods(beanName, wrappedBean, beanDefinition);
		
		wrappedBean = applyBeanPostProcessorsAfterInitialization(bean, beanName);
		return wrappedBean;
	}

	private void invokeInitMethods(String beanName, Object wrappedBean, BeanDefinition beanDefinition) {
	}

	private Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName) {
		Object result = existingBean;
		for (BeanPostProcessor beanPostProcessor : getPostProcessorList()) {
			Object  current = beanPostProcessor.postProcessAfterInitialization(result, beanName);
			if (null == current) {
				return result;
			}
			result = current;
		}
		return result;
	}

	private Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName) {
		Object result = existingBean;
		for (BeanPostProcessor  beanPostProcessor : getPostProcessorList()) {
			Object current = beanPostProcessor.postProcessBeforeInitialization(result, beanName);
			if (null == current) {
				return result;
			}
			result = current;
		}
		return result;
	}
	
	protected Object createBeanInstance(BeanDefinition beanDefinition, String beanName, Object[] args) {
		Constructor constructorToUse = null;
		Class<?> beanClass = beanDefinition.getBeanClass();
		Constructor<?>[] declaredConstructors = beanClass.getDeclaredConstructors();
		for (Constructor<?> constructor : declaredConstructors) {
			if (null != args && constructor.getParameterTypes().length == args.length) {
				constructorToUse = constructor;
				break;
			}
		}
		
		return getInstantiationStrategy().instantiate(beanDefinition,beanName, constructorToUse, args);
	}

	protected void applyPropertyValues(String beanName, Object bean, BeanDefinition beanDefinition) {
		try {
			PropertyValues propertyValues = beanDefinition.getPropertyValues();

			for (PropertyValue propertyValue : propertyValues.getPropertyValues()) {
				String name = propertyValue.getName();
				Object value = propertyValue.getValue();

				if (value instanceof BeanReference) {
					BeanReference beanReference = (BeanReference) value;
					value = getBean(beanReference.getBeanName());
				}

				BeanUtil.setFieldValue(bean, name, value);
			}	
		} catch (Exception e) {
			throw new BeansException("Error setting property values：" + beanName);
		}
	}
}
